#include "util.h"
#include <set>

#include "Class.h"
#include "Class.h"
#include "Method.h"
#include <string.h>
Class::Class(){
    
}
void Class::setFinal(bool final){
    Class::final=final;
}
void Class::setName(string name){
    if(wasAlreadyNamed) image->exception("Class was already given a name");
    image->addClass(name,this);
    wasAlreadyNamed=true;
    Class::name=name;
}
string Class::getName(){
    return name;
}
bool Class::isFinal(){
    return final;
}
void Class::setParent(Class* cls){
    if(cls==NULL) return;
    if(cls->isFinal()){
        image->exception("Parent is final");
    }
    if(parentClass!=NULL){
        parentClass->subclasses.erase(this);
    }
    parentClass=cls;
    parentClass->subclasses.insert(this);
    size=-1;
}
bool Class::hasField(string field){
    return (fields.find(field)!=fields.end())||
                ((parentClass!=NULL)&&parentClass->hasField(field));
}
bool Class::hasDirectlyDefined(string method){
    //is used by find method, in order not to recurse itself
    return (selectors.find(method)!=selectors.end())||
            ((parentClass!=NULL)&&parentClass->hasDirectlyDefined(method));
}
Method* Class::findMethod(string selector){
   //find the method in this classes implemented methods
   if(selectors.find(selector)!=selectors.end()){
       return selectors[selector];
   }
   //use custom-provided search method findMethod: if this is modified subclass of Class
    if(this->getClass(image)!=NULL && this->getClass(image)!=image->clsClass){
        //skip findMethod: if we are of class NULL or Class or we do not implement it
        if(this->getClass(image)->hasDirectlyDefined("findMethod:")){
            //printf("Selector %s",selector.c_str());
            Object* args[1];
            args[0]=makeString(image,(char*)selector.c_str());
            Method* m=(Method*)this->send(image,"findMethod:",1,args);
            //if(m!=NULL) printf("Selector found by.\n");
            if(m!=NULL) return m;
        }
    }
   //look in the parent class if possible
   if(parentClass==NULL){
        return NULL;
   }else{
       return parentClass->findMethod(selector);
   }
}
Object* Class::sendClass(Object* self,string selector,int argc,Object** argv){
    if(selector=="initialize"){
        //TODO: this is a hack, in future it should be possible to call overriden method
        // so the construction of the object will be solved this way
        Class* init=this;
        while(true){
            if(init->selectors.find(selector)!=init->selectors.end()){
                Method* m=init->selectors[selector];
                m->execute(image,self,argc,argv);
            }
            if(init->parentClass!=NULL){
                init=init->parentClass;
            }else{
                break;
            }
        }
    }else{
        Method* m;
        m=findMethod(selector);
        if(m==NULL) image->exception("Method does not exist "+selector);
        return m->execute(image,self,argc,argv);
    }
}
void Class::initializeObject(Object* o,bool callInitMethod){
    //TODO: check memory leaks here
    o->vptr=clsVptr;
    if(callInitMethod){
        o->send(image,"initialize",0,NULL);
        /* some objects need to call the native constructor in-order to initialize,
         * and it erases vptr, so we set it again */
        o->vptr=clsVptr;
    }
}
Object* Class::newInstance(bool call){
   Object* obj=(Object*)image->alloc(getSize());
   initializeObject(obj,call);
   return obj;
}
void Class::addMethod(Method* m){
   methods.push_back(m);
   selectors[m->getSelector()]=m;
   //printf("Adding method %s.\n",m->getSelector().c_str());
   //genVPTR(true);
}
void Class::genVPTR(bool shoudDelete){
   if(shoudDelete) delete[] clsVptr;
   int methods=0;
   clsVptr=new VPTR_Entry[methods+1];
   clsVptr[0].method=(Method*)this;
}
int sizeOfCF(ClassField* cf){
   if(cf->customObject){
      return cf->sizeOverride;
   }
   return sizeof(Object*);
}
Object* Class::get(Object* obj,string field){
    //ensure offsets are valid
    getSize();
    bool found=fields.find(field)!=fields.end();
    if(found){
        Object** ref=(Object**) obj->dataAt(fields[field]->offset);
        return *ref;
    }else if(parentClass!=NULL){
        return parentClass->get(obj,field);
    }else{
        image->exception("Field does not exist "+field);
    }
}
void Class::set(Object* obj,string field,Object* value){
        //ensure offsets are valid
    getSize();
    bool found=fields.find(field)!=fields.end();
    if(found){
        Object** ref=(Object**) obj->dataAt(fields[field]->offset);
        (*ref)=value;
    }else if(parentClass!=NULL){
        return parentClass->set(obj,field,value);
    }else{
        image->exception("Field does not exist "+field);
    }
}
void Class::addField(string name,Type* type,int flags){
    ClassField* cf=new ClassField();
    cf->name=name;
    cf->type=type;
    cf->customObject=false;
    structure.push_back(cf);
    fields[name]=cf;
    size=-1;
}
void Class::addCustomField(string name,int size){
   ClassField* cf=new ClassField();
   cf->name=name;
   cf->type=NULL;
   cf->customObject=true;
   cf->sizeOverride=size;
   structure.push_back(cf);
   size=-1;
}
void Class::recomputeSize(){
   int size=(parentClass==NULL)?sizeof(Object):parentClass->getSize();
   list<ClassField*>::iterator it=structure.begin();
   while(it!=structure.end()){
      (*it)->offset=size;
      size+=sizeOfCF(*it);
      it++;
   }
   Class::size=size;
   std::set<Class*>::iterator sit=subclasses.begin();
    while(subclasses.end()!=sit){
        (*sit)->size=-1;
        sit++;
    }
}
int Class::getSize(){
   if(size==-1){
      recomputeSize();
   }
   if(size==-1){
       printf("Hasdsad");
       recomputeSize();
   }
   return size;
}
Object* primitive_class_initialize(Image* image,Object* self,int argc,Object** argv){
    if(argc!=0) image->exception("Class.initialize requires 0 arguments");
    Class* me=new(self) Class();
    me->wasAlreadyNamed=false;
    me->final=false;
    me->parentClass=NULL;
    me->setParent(image->clsObject);
    me->genVPTR(false);
    me->image=image;
    me->size=-1;
}
Object* primitive_class_set_parent(Image* image,Object* self,int argc,Object** argv){
    if(argc!=1) image->exception("Class.setParent: requires one argument");
    ((Class*)self)->setParent((Class*)argv[0]);
    return NULL;
}
Object* primitive_class_set_name(Image* image,Object* self,int argc,Object** argv){
    ((Class*)self)->setName(inspectString(argv[0]));
    return NULL;
}

Object* primitive_class_new(Image* image,Object* self,int argc,Object** argv){
    Class* me=(Class*)self;
    return me->newInstance();
}
Object* primitive_class_add_field(Image* image,Object* self,int argc,Object** argv){
    Class* me=(Class*)self;
    string name=inspectString(argv[0]);
    me->addField(name,NULL,FIELD_ORDINARY);
    return NULL;
}
Object* primitive_class_add_method(Image* image,Object* self,int argc,Object** argv){
    Class* me=(Class*)self;
    me->addMethod((Method*)argv[0]);
    return NULL;
}